import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, LabelEncoder
import seaborn as sns
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.naive_bayes import GaussianNB
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
from sklearn.model_selection import GridSearchCV

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号

# 读取数据
data = {
    '债券简称': ['G21苏林1', 'G21综能1', 'G21华新1', 'G21华新2', '21北电', 'PR文1A1', 'PR文1A2', 'PR文1B', 'GC华能04', 'GC太科01', '21昆明轨道', 'G苏天2优', 'G苏天2次', 'G融租A1', 'G融租A2', 'G融租次'],
    '计划发行规模（亿）': [2.0000, 10.0000, 10.0000, 10.0000, 5.0000, 2.5200, 2.8000, 0.9800, 20.0000, 1.0000, 10.0000, 10.1200, 0.5300, 7.6000, 7.5000, 0.8000],
    '发行金额上限（亿）': [2.0000, 10.0000, 10.0000, 10.0000, None, None, None, None, 20.0000, 1.0000, 15.0000, None, None, None, None, None],
    '债券评级': [None, 'AA+', 'AAA', 'AAA', None, 'AAA', 'AAA', 'AA+', 'AAA', 'AA', 'AAA', 'AAA', None, 'AAA', 'AAA', None],
    '票面利率（%）': [None, 3.27, 3.08, 3.10, 3.82, 3.50, 3.88, 4.50, 3.10, 3.99, 4.08, 3.50, None, None, None, None]
}

df = pd.DataFrame(data)

# 使用SimpleImputer对缺失值进行填充
imputer = SimpleImputer(strategy='median')
df['发行金额上限（亿）'] = imputer.fit_transform(df['发行金额上限（亿）'].values.reshape(-1, 1))

# 对债券评级和票面利率进行填充，可以使用不同的策略，这里使用最频繁值填充
imputer = SimpleImputer(strategy='most_frequent')
df['债券评级'] = imputer.fit_transform(df['债券评级'].values.reshape(-1, 1)).reshape(-1)

# 由于票面利率是目标变量，缺失值较多且无法填充，将含有缺失值的行删除
df.dropna(subset=['票面利率（%）'], inplace=True)

# 对债券简称进行编码
label_encoder = LabelEncoder()
df['债券简称'] = label_encoder.fit_transform(df['债券简称'])

# 对债券评级进行编码
df['债券评级'] = label_encoder.fit_transform(df['债券评级'])

# 对规模和发行金额进行标准化
scaler = StandardScaler()
df[['计划发行规模（亿）', '发行金额上限（亿）']] = scaler.fit_transform(df[['计划发行规模（亿）', '发行金额上限（亿）']])

# 可视化特征之间的关系
sns.pairplot(df, vars=['计划发行规模（亿）', '发行金额上限（亿）', '债券评级', '票面利率（%）'])
plt.show()

# 可视化特征分布
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
sns.histplot(df['计划发行规模（亿）'], kde=True, ax=axes[0, 0]).set_title('计划发行规模分布')
sns.histplot(df['发行金额上限（亿）'], kde=True, ax=axes[0, 1]).set_title('发行金额上限分布')
sns.countplot(x='债券评级', data=df, ax=axes[1, 0]).set_title('债券评级分布')
sns.histplot(df['票面利率（%）'], kde=True, ax=axes[1, 1]).set_title('票面利率分布')
plt.show()

# 可视化特征之间的相关性
numerical_columns = ['计划发行规模（亿）', '发行金额上限（亿）', '债券评级', '票面利率（%）']
correlation_matrix = df[numerical_columns].corr()

plt.figure(figsize=(8, 6))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', fmt='.2f')
plt.title('特征之间的相关性热力图')
plt.show()

# 模型训练和性能可视化
X = df[['债券简称', '计划发行规模（亿）', '发行金额上限（亿）']]
y = df['债券评级']

# 将数据分割为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 构建决策树模型
decision_tree_model = DecisionTreeClassifier()
decision_tree_model.fit(X_train, y_train)
y_pred_dt = decision_tree_model.predict(X_test)
accuracy_dt = accuracy_score(y_test, y_pred_dt)
print("决策树模型准确率:", accuracy_dt)

# 构建随机森林模型
random_forest_model = RandomForestClassifier()
random_forest_model.fit(X_train, y_train)
y_pred_rf = random_forest_model.predict(X_test)
accuracy_rf = accuracy_score(y_test, y_pred_rf)
print("随机森林模型准确率:", accuracy_rf)

# 构建贝叶斯模型
naive_bayes_model = GaussianNB()
naive_bayes_model.fit(X_train, y_train)
y_pred_nb = naive_bayes_model.predict(X_test)
accuracy_nb = accuracy_score(y_test, y_pred_nb)
print("贝叶斯模型准确率:", accuracy_nb)

# 定义模型和参数网格
models = {
    'Decision Tree': DecisionTreeClassifier(),
    'Random Forest': RandomForestClassifier(),
    'Naive Bayes': GaussianNB()
}

param_grids = {
    'Decision Tree': {'max_depth': [3, 5, 7]},
    'Random Forest': {'n_estimators': [50, 100, 200]},
    'Naive Bayes': {}
}

# 交叉验证调优模型参数
best_models = {}
for model_name in models:
    model = models[model_name]
    param_grid = param_grids[model_name]
    grid_search = GridSearchCV(model, param_grid, cv=5, scoring='accuracy')
    grid_search.fit(X_train, y_train)
    best_models[model_name] = grid_search.best_estimator_

# 计算性能指标
for model_name in best_models:
    model = best_models[model_name]
    y_pred = model.predict(X_test)
    accuracy = accuracy_score(y_test, y_pred)
    precision = precision_score(y_test, y_pred, average='weighted')
    recall = recall_score(y_test, y_pred, average='weighted')
    f1 = f1_score(y_test, y_pred, average='weighted')
    print(f"{model_name}模型性能指标:")
    print(f"准确率: {accuracy}")
    print(f"精确率: {precision}")
    print(f"召回率: {recall}")
    print(f"F1值: {f1}")
    print()

# 存储模型性能数据
performance_data = {
    '模型': list(best_models.keys()),
    '准确率': [accuracy_score(y_test, best_models[model].predict(X_test)) for model in best_models],
    '精确率': [precision_score(y_test, best_models[model].predict(X_test), average='weighted') for model in best_models],
    '召回率': [recall_score(y_test, best_models[model].predict(X_test), average='weighted') for model in best_models],
    'F1值': [f1_score(y_test, best_models[model].predict(X_test), average='weighted') for model in best_models]
}
performance_df = pd.DataFrame(performance_data)

# 绘制条形图
plt.figure(figsize=(10, 6))
metrics = ['准确率', '精确率', '召回率', 'F1值']
for i, metric in enumerate(metrics):
    plt.subplot(2, 2, i+1)
    sns.barplot(x='模型', y=metric, data=performance_df)
    plt.title(f'不同模型的{metric}')

plt.tight_layout()
plt.show()

from sklearn.metrics import confusion_matrix, classification_report
# 输出混淆矩阵
conf_matrix_dt = confusion_matrix(y_test, y_pred_dt)
print("决策树模型混淆矩阵:")
print(conf_matrix_dt)

conf_matrix_rf = confusion_matrix(y_test, y_pred_rf)
print("随机森林模型混淆矩阵:")
print(conf_matrix_rf)

conf_matrix_nb = confusion_matrix(y_test, y_pred_nb)
print("贝叶斯模型混淆矩阵:")
print(conf_matrix_nb)

# 输出分类报告
class_report_dt = classification_report(y_test, y_pred_dt)
print("决策树模型分类报告:")
print(class_report_dt)

class_report_rf = classification_report(y_test, y_pred_rf)
print("随机森林模型分类报告:")
print(class_report_rf)

class_report_nb = classification_report(y_test, y_pred_nb)
print("贝叶斯模型分类报告:")
print(class_report_nb)

